package com.example.client;

import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.util.*;

import com.fasterxml.jackson.core.JsonProcessingException;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
import com.fasterxml.jackson.databind.ObjectMapper;
import javafx.stage.FileChooser;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONObject;

@ClientEndpoint
public class ClientController {
    /**
     * 翻译
     */
    public ImageView translate;

    /**
     * 显示我的名字
     */
    @FXML
    private Label showMyName;

    /**
     * 显示我的账号
     */
    @FXML
    private Label showMyAccount;

    /**
     * 聊天框上好友名字
     */
    @FXML
    private Label showFriendName;

    /**
     * 搜索输入框
     */
    @FXML
    private TextField textSearch;

    /**
     * 我的头像
     */
    @FXML
    private ImageView myImage;

    /**
     * 消息输入框
     */
    @FXML
    private TextArea textMsg;

    /**
     * 左侧放置好友
     */
    @FXML
    private VBox friendVBox;

    /**
     * 左侧放置好友的
     */
    @FXML
    private ScrollPane friendSCP;

    /**
     * 右侧放置聊天消息
     */
    @FXML
    private VBox msgVBox;

    /**
     * 右侧放置聊天消息的
     */
    @FXML
    private ScrollPane msgSCP;

    /**
     * 发送按钮
     */
    @FXML
    private Button btnSend;

    /**
     * 文件
     */
    @FXML
    private ImageView file;

    /**
     * 消息面板滚动条是否刷新
     */
    private boolean updateFlag = false;

    //每个客户的用户名
    private static String username;

    private Session session;
    private static final int PART_SIZE = 1024 * 1024; // 1MB part size
    //    private static final String WEBSOCKET_URI = "ws://139.9.185.3:8080/websocket/";
    private static final String WEBSOCKET_URI = "ws://127.0.0.1:8080/websocket/";
    private static final String UPLOAD_URL = "http://127.0.0.1:8080/file-upload";
//    private static final String UPLOAD_URL = "http://139.9.185.3:8080/file-upload";

    public void initialize() throws IOException {
        showGetUsernameDialog();
        showMyName.setText(username);
        connect();
    }

    private void showGetUsernameDialog() {
        TextInputDialog dialog = new TextInputDialog();
        dialog.setTitle("Enter Username");
        dialog.setHeaderText(null);
        dialog.setContentText("Please enter your username:");
        dialog.initOwner(null);
        getUsername(dialog);
    }


    public static void setUsername(String username) {
        ClientController.username = username;
    }

    private void getUsername(TextInputDialog dialog) {
        Optional<String> result = dialog.showAndWait();

        result.ifPresent(username -> {
            if (username.isEmpty()) {
                Alert alert = new Alert(Alert.AlertType.ERROR);
                alert.setTitle("Error");
                alert.setHeaderText(null);
                alert.setContentText("Username cannot be empty!");

                alert.showAndWait();
                getUsername(dialog);
            } else {
                ClientController.setUsername(username);
            }
        });
    }

    @FXML
    private void clickImageButton(MouseEvent event) {

    }

    @FXML
    private void chooseFileButton(MouseEvent event) {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Choose File");
        File selectedFile = fileChooser.showOpenDialog(null); // 显示文件选择器

        if (selectedFile != null) {
            long length = selectedFile.length();
            System.out.println(length);
            sendBigFile(selectedFile);
//            // 发送HTTP请求
//            Message msg = sendHttpRequest(selectedFile);
//            if(msg!=null){
//                showSendMsg(msg);
//            }
        }
    }

    private void sendBigFile(File file) {
        System.out.println("111");
        try {
            Map<String, Object> initResponse = initMultiPartUpload(file);
            System.out.println("000"+initResponse);
            if (initResponse != null) {
                Map<String, Object> dataMap = (Map<String, Object>) initResponse.get("data");
                List<String> partList = (List<String>) dataMap.get("uploadUrls");
                String uploadId = (String) dataMap.get("uploadId");
                String objectName = (String) dataMap.get("objectName");

                System.out.println(partList);
                System.out.println(uploadId);
                System.out.println(objectName);

                uploadFileInParts(file, partList, uploadId);

                dataMap = completeMultiPartUpload(objectName, uploadId);
                if ((boolean)dataMap.get("success")) {
                    System.out.println("File uploaded successfully!");
                    String url = (String) dataMap.get("url");
                    System.out.println(url);
                } else {
                    System.out.println("Failed to complete file upload.");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Map<String, Object> initMultiPartUpload(File file) throws IOException {
        System.out.println("222");
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost("http://127.0.0.1:8080/multipart/init");

            JSONObject requestParam = new JSONObject();
            requestParam.put("path", "test");
            requestParam.put("filename", file.getName());
            requestParam.put("contentType", "application/octet-stream");
            requestParam.put("md5", "");  // Add MD5 calculation if needed
            requestParam.put("partCount", (int) Math.ceil((double) file.length() / PART_SIZE));
            StringEntity requestEntity = new StringEntity(requestParam.toString(), "UTF-8");
            httpPost.setEntity(requestEntity);
            // 设置请求头的 Content-Type 为 application/json
            httpPost.setHeader("Content-Type", "application/json");
            System.out.println(requestParam);
            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                String jsonResponse = EntityUtils.toString(response.getEntity());
                ObjectMapper objectMapper = new ObjectMapper();
                return objectMapper.readValue(jsonResponse, Map.class);
            }
        }
    }

    private static void uploadFileInParts(File file, List<String> presignedUrls, String uploadId) throws IOException {
        try (CloseableHttpClient httpClient = HttpClients.createDefault();
             FileInputStream fis = new FileInputStream(file)) {

            byte[] buffer = new byte[PART_SIZE];
            int bytesRead;
            int partNumber = 1;

            while ((bytesRead = fis.read(buffer)) != -1) {
                String presignedUrl = presignedUrls.get(partNumber - 1);
                HttpPost httpPost = new HttpPost("http://127.0.0.1:8080/multipart/upload");

                MultipartEntityBuilder builder = MultipartEntityBuilder.create();
                builder.addTextBody("uploadId", uploadId);
                builder.addTextBody("partNumber", String.valueOf(partNumber));
                builder.addTextBody("path", presignedUrl);
                builder.addBinaryBody("file", Arrays.copyOf(buffer, bytesRead), ContentType.DEFAULT_BINARY, file.getName());

                httpPost.setEntity(builder.build());

                try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                    if (response.getStatusLine().getStatusCode() != 200) {
                        throw new IOException("Failed to upload part " + partNumber);
                    }
                }

                partNumber++;
            }
        }
    }


    private static Map<String, Object> completeMultiPartUpload(String objectName, String uploadId) throws IOException {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPut httpPut = new HttpPut("http://127.0.0.1:8080/multipart/complete");

            JSONObject requestParam = new JSONObject();
            requestParam.put("objectName", objectName);
            requestParam.put("uploadId", uploadId);

            StringEntity requestEntity = new StringEntity(requestParam.toString(), "UTF-8");
            httpPut.setEntity(requestEntity);

            try (CloseableHttpResponse response = httpClient.execute(httpPut)) {
                String jsonResponse = EntityUtils.toString(response.getEntity());
                ObjectMapper objectMapper = new ObjectMapper();
                Map<String, Object> responseMap = objectMapper.readValue(jsonResponse, Map.class);
                return (Map<String, Object>) responseMap.get("data");
            }
        }
    }





    private Message sendHttpRequest(java.io.File file) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(UPLOAD_URL);

            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            builder.addBinaryBody("file", file, ContentType.DEFAULT_BINARY, file.getName());

            HttpEntity multipart = builder.build();
            httpPost.setEntity(multipart);

            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                HttpEntity responseEntity = response.getEntity();
                if (responseEntity != null) {
                    String jsonResponse = EntityUtils.toString(responseEntity);

                    // 解析JSON响应
                    ObjectMapper objectMapper = new ObjectMapper();
                    FileUploadResponse fileUploadResponse = objectMapper.readValue(jsonResponse, FileUploadResponse.class);

                    // 使用解析后的数据
                    int errno = fileUploadResponse.getErrno();
                    if(errno == 0){
                        String fileUrl = fileUploadResponse.getData().getUrl();
                        System.out.println(fileUrl);
                        Message msg = new Message(username, "hza", "", "FILE", file.getName()+"@@@"+fileUrl);
                        sendMessage(msg.toString());
                        return msg;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @FXML
    public void translateButton(MouseEvent mouseEvent) {
        TranslateController translateController = new TranslateController();
        translateController.translate();
    }



    @FXML // 发送消息
    private void sendMessageButton(ActionEvent event) {
        sendMsg();
    }



    /**
     * 发送消息
     */
    void sendMsg() {
        //todo: 还要确定具体要发给谁
        Message msg = new Message(username, "hza", "", "TEXT", textMsg.getText());
        sendMessage(msg.toString());
        showSendMsg(msg);
        System.out.println("发送消息：" + msg);
        updateFlag = true;
        msgSCP.setVvalue(1);
        this.textMsg.clear();
    }

    /**
     * 发消息
     * @param msg
     */
    void showSendMsg(Message msg) {
        AnchorPane an = new AnchorPane();
        an.setPrefWidth(590);
        //ImageView iv = new ImageView(new Image("touxiang/u=1437473548,3272300633&fm=26&gp=0.jpg"));
        //iv.setFitWidth(40);
        //iv.setFitHeight(40);

        Label l1 = new Label(username);
        l1.setFont(new Font(13));
        l1.setTextAlignment(TextAlignment.RIGHT);
        l1.setMaxWidth(400);

        if(msg.getType().equals("TEXT")){
            Label l2 = new Label(msg.getMessage());
            l2.setFont(new Font(18));
            l2.setStyle("-fx-background-color: #ffffff");
            l2.setPadding(new Insets(5, 10, 5, 10));
            l2.setWrapText(true);
            l2.setMaxWidth(400);

            //an.getChildren().addAll(iv,l1,l2);
            an.getChildren().addAll(l1,l2);

            //AnchorPane.setLeftAnchor(iv, 537.0);
            //AnchorPane.setTopAnchor(iv, 25.0);
            AnchorPane.setRightAnchor(l1, 56.0);
            AnchorPane.setTopAnchor(l1, 25.0);

            AnchorPane.setRightAnchor(l2, 56.0);
            AnchorPane.setTopAnchor(l2, 55.0);
        }
        else if(msg.getType().equals("FILE")){
            String fileString = msg.getMessage();
            String[] parts = fileString.split("@@@");
            String fileName = parts[0];
            String fileUrl = parts[1];
            Hyperlink link = new Hyperlink(fileName);
            link.setOnAction(event -> {
                openFileUrlInBrowser(fileUrl);
            });

            // 添加到 AnchorPane
            an.getChildren().addAll(l1, link);

            AnchorPane.setRightAnchor(l1, 56.0);
            AnchorPane.setTopAnchor(l1, 25.0);
            AnchorPane.setRightAnchor(link, 56.0);
            AnchorPane.setTopAnchor(link, 55.0);
        }

        msgVBox.getChildren().add(an);
    }

    /**
     * 收消息
     * @param msg
     */
    void showRecvMsg(Message msg) {
        Platform.runLater(() -> {
            AnchorPane an = new AnchorPane();
            an.setPrefWidth(590);
            //ImageView iv = new ImageView(new Image("touxiang/timg.jpg"));
            //iv.setFitWidth(40);
            //iv.setFitHeight(40);

            Label l1 = new Label(msg.getSender());
            l1.setFont(new Font(13));
            l1.setMaxWidth(400);

            if(msg.getType().equals("TEXT")){
                Label l2 = new Label(msg.getMessage());
                l2.setFont(new Font(18));
                l2.setStyle("-fx-background-color: #55a3ec");
                l2.setPadding(new Insets(5, 10, 5, 10));
                l2.setMaxWidth(400);
                l2.setWrapText(true);

                //an.getChildren().addAll(iv,l1,l2);
                an.getChildren().addAll(l1,l2);

                //AnchorPane.setLeftAnchor(iv, 22.0);
                //AnchorPane.setTopAnchor(iv, 23.0);

                AnchorPane.setLeftAnchor(l1, 76.0);
                AnchorPane.setTopAnchor(l1, 23.0);

                AnchorPane.setLeftAnchor(l2, 76.0);
                AnchorPane.setTopAnchor(l2, 51.0);
            }
            else if (msg.getType().equals("FILE")){
                String fileString = msg.getMessage();
                String[] parts = fileString.split("@@@");
                String fileName = parts[0];
                String fileUrl = parts[1];
                Hyperlink link = new Hyperlink(fileName);
                link.setOnAction(event -> {
                    openFileUrlInBrowser(fileUrl);
                });

                // 添加到 AnchorPane
                an.getChildren().addAll(l1, link);

                AnchorPane.setLeftAnchor(l1, 76.0);
                AnchorPane.setTopAnchor(l1, 23.0);
                AnchorPane.setLeftAnchor(link, 76.0);
                AnchorPane.setTopAnchor(link, 51.0);
            }
            msgVBox.getChildren().add(an);
        });
    }

    void openFileUrlInBrowser(String fileUrl) {
        try {
            java.awt.Desktop.getDesktop().browse(new java.net.URI(fileUrl));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*下面是有关websocket的方法*/
    public void connect() {
        try {
            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
            URI uri = URI.create(WEBSOCKET_URI + username);
            container.connectToServer(this, uri);
            System.out.println("连接成功！");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void sendMessage(String message) {
        try {
            if (session != null && session.isOpen()) {
                session.getBasicRemote().sendText(message);
            } else {
                System.out.println("Session is not open. Can't send message.");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        System.out.println("Connected to the WebSocket server");
    }

    @OnMessage
    public void onMessage(String message) throws JsonProcessingException {
        System.out.println("Received message from server: " + message);

        ObjectMapper objectMapper = new ObjectMapper();
        Message msg = objectMapper.readValue(message, Message.class);
        if(username.equals(msg.getReceiver())){
            showRecvMsg(msg);
        }
    }

    @OnError
    public void onError(Throwable error) {
        error.printStackTrace();
    }


}
